package cn.sunxiansheng.redis.utils;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Redis基础工具类，提供一些基础的操作
 *
 * @Author sun
 * @Create 2024/11/14 14:25
 * @Version 1.0
 */
public class RBase {

    protected RedisTemplate<String, Object> redisTemplate;

    public RBase(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 默认的key分隔符
     */
    private static final String DEFAULT_KEY_SEPARATOR = ":";

    /**
     * 构建缓存key（使用默认的key分隔符）
     *
     * @param parts 多个字符串拼接成缓存key
     * @return 拼接后的缓存key
     */
    public String buildKeyByDefaultSeparator(String... parts) {
        return String.join(DEFAULT_KEY_SEPARATOR, parts);
    }

    /**
     * 构建缓存key（使用自定义的key分隔符）
     *
     * @param separator 自定义的key分隔符
     * @param parts     多个字符串拼接成缓存key
     * @return 拼接后的缓存key
     */
    public String buildKeyByCustomSeparator(String separator, String... parts) {
        return String.join(separator, parts);
    }

    /**
     * 删除单个key
     *
     * @param key 键
     * @return 如果键不存在，返回false；如果删除成功，返回true
     */
    public Boolean delete(String key) {
        if (key == null || key.isEmpty()) {
            throw new IllegalArgumentException("Key cannot be null or empty");
        }
        return redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     *
     * @param keys 键的集合
     * @return 成功删除的键的数量，键不存在不计数
     */
    public Long deleteBatch(Collection<String> keys) {
        if (keys == null || keys.isEmpty()) {
            return 0L;
        }
        return redisTemplate.delete(keys);
    }

    /**
     * 指定缓存失效时间
     *
     * @param key     键
     * @param timeout 失效时间（小于等于0 表示 永久有效）
     * @param unit    时间单位
     * @return 设置成功返回true，失败返回false
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        if (key == null || key.isEmpty()) {
            throw new IllegalArgumentException("Key cannot be null or empty");
        }
        if (timeout <= 0) {
            // 或直接返回 true，表示设置为永久有效
            return false;
        }
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 指定缓存失效时间（时间单位：秒）
     *
     * @param key     键
     * @param timeout 失效时间（单位：秒，小于等于0 表示 永久有效）
     * @return 设置成功返回true，失败返回false
     */
    public Boolean expire(String key, long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取缓存失效时间
     *
     * @param key 键
     * @return 缓存失效时间（秒）。键不存在返回-2，键存在但没有设置过期时间返回-1
     */
    public Long getExpire(String key) {
        if (key == null || key.isEmpty()) {
            throw new IllegalArgumentException("Key cannot be null or empty");
        }
        return redisTemplate.getExpire(key);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return 存在返回true，不存在返回false
     */
    public Boolean hasKey(String key) {
        if (key == null || key.isEmpty()) {
            throw new IllegalArgumentException("Key cannot be null or empty");
        }
        return redisTemplate.hasKey(key);
    }

    /**
     * 转换缓存中的对象为指定类型
     *
     * @param value 缓存中的对象
     * @param type  要转换的目标类型
     * @param <T>   返回的泛型类型
     * @return 转换后的对象
     * @throws ClassCastException 如果对象不能转换为指定类型
     */
    protected <T> T convertValue(Object value, Class<T> type) {
        if (value == null) {
            return null;
        }

        // Long类型的特殊处理，因为Redis中的数据会自动反序列化为Integer
        if (type == Long.class && value instanceof Integer) {
            return type.cast(((Integer) value).longValue());
        }

        if (type.isInstance(value)) {
            return type.cast(value);
        } else {
            throw new ClassCastException("Redis中的对象运行类型与请求转换的类型不匹配，请优先检查Redis配置类是否保存了对象原始信息！期待类型："
                    + type.getName() + "，实际类型："
                    + value.getClass().getName());
        }
    }

    /**
     * 执行Lua脚本
     *
     * @param script   Lua脚本内容
     * @param resultType 返回类型的Class对象
     * @param keys     Redis中脚本执行时需要的键
     * @param args     脚本执行时需要的参数
     * @param <T>      返回类型的泛型
     * @return 执行结果
     */
    public <T> T executeLuaScript(String script, Class<T> resultType, Collection<String> keys, Object... args) {
        if (script == null || script.isEmpty()) {
            throw new IllegalArgumentException("Lua脚本内容不能为空");
        }
        if (keys == null || keys.isEmpty()) {
            throw new IllegalArgumentException("Redis keys 不能为空");
        }

        // 创建Lua脚本对象
        RedisScript<T> redisScript = new DefaultRedisScript<>(script, resultType);

        // 执行Lua脚本
        return redisTemplate.execute(redisScript, (List<String>) keys, args);
    }
}